以下是某種情境假設,在「app 迭代的過程中,Unit testing 可以怎麼幫你」
這個物件可以這樣宣告
struct Customer {
let firstName: String
let lastName: String
var displayName: String {
return firstName + lastName
}
}
• App 推出後大賣,不過!歐美 sales 反應,他們的客戶有些有 middle name,而且被強烈要求在 UI 上呈現出 middle name。所以需要在 user 顯示名稱那邊,顯示 middle name。因為有一個成員剛進來
struct Customer {
let firstName: String
let lastName: String
var middleName: String?
var displayName: String {
/// 因為這任務交給一個剛來的人寫,所以那個人就這樣寫了
return firstName + middleName! + lastName
}
}
果不其所然,沒有意外的話,就要發生意外了。新的使用者,可能都沒問題,但如果是舊版本的使用者升級到新版,原來的程式並沒有存下 middleName,因為那個時期這個 property 不存在,所以每個升級的使用者都遇到了 app 閃退的問題。
修復後,這個物件宣告長這樣。
struct Customer {
let firstName: String
let lastName: String
var middleName: String?
var displayName: String {
/// 如果是有中間名的 user,就呈現中間名
if let middleName = middleName {
return firstName + middleName + lastName
}
/// 如果是沒有中間名的,就不處理 middle name
return firstName + lastName
}
}
然後再經過幾次行銷活動,這個 app 更紅了。某天你被叫進需求會議,接到了亞洲區 Sales 的需求。
• App 修改完後更大賣,但亞洲區 sales 反應,姓要在名字前面。且中間不要空格
這邊就省略程式碼,不斷的進行時間快轉。
下一個需求
• 日本 sales 反應。日本 UI 如果呈現姓加名字黏在一起,會讓特殊姓氏不知道怎麼念,而且日本人習慣姓和名中間空一格
下一個需求
• 馬來西亞 sales 反應,他們的中間名可能有五個起跳,但習慣上可能用不同於其他國家的方式進行呈現。所以需要先判斷 x 條件,然後符合 y 條件時,呈現 z UI。
上面所述的這個狀況,是會不斷的發生的。改需求,改文案,增加銷售地區,因為增加銷售地區所以增加了新的需求。當你手上的程式碼註定會碰到這樣的變化,Unit testing 會在哪些狀況下幫到你呢?
我們遇到了單一 data model 在不同 locale 時,要呈現不同的排列方式。在這個情況下, Unit testing 會怎樣幫助你的專案呢?
///
struct Customer {
let firstName: String
let lastName: String
var displayName: String {
return firstName + lastName
}
}
針對 Customer 物件的第一版 Unit testing
在 Xcode 中,要讓一個 func 能在 unit testing 中被認知到,func 宣告時前四個字母要是 test,才能被模組認知到這是 test。這邊宣告 func testCustomerDisplayName()
。按一下 cmd + B,這時這一行左方的行數號碼,應該會變成一個菱形,表示這是測試 func。
final class CustomerNameTests: XCTestCase {
override func setUpWithError() throws {}
override func tearDownWithError() throws {}
func testCustomerDisplayName() {
let customer = Customer(firstName: "Foo", lastName: "Bar")
let answer = "FooBar"
/// 第一次跑 unit testing,會讓 testing failed,來確保 unit testing 是正確運作的
XCTAssertEqual(customer.displayName, answer)
}
}
開始跑完 Unit testing,你會看到錯誤,表示 Unit testing 有正確運作,未來如果 data model 有錯時,這個 Unit testing 可以正確的告訴你程式有錯誤。接下來,我們把 XCTAssertEqual 的第二個參數改成 answer。改完後跑一次 Unit testing,這時原來的錯誤就消失了。
如果那位工程師擴充了 middleName, 並使用了 force unwrap 在 displayName 中,會怎樣呢?
/// 第二版 Customer 物件,而 property 的擴充交給另一個工程師
struct Customer {
let firstName: String
let lastName: String
var middleName: String?
var displayName: String {
/// 因為這任務交給一個剛來的人寫,所以那個人就這樣寫了
return firstName + middleName! + lastName
}
}
從圖上可以看到,Unit testing 運行時,他在這一行會停下來,因 Unit testing crash 了。這就表示這段程式碼在產品上,也可能造成 app 閃退。
修改後的程式碼
struct Customer {
let firstName: String
let lastName: String
var middleName: String?
var displayName: String {
/// 如果是有中間名的 user,就呈現中間名
if let middleName = middleName {
return firstName + middleName + lastName
}
/// 如果是沒有中間名的,就不處理 middle name
return firstName + lastName
}
}
運行後,都是綠燈,表示程式碼是安全的。但現在的 Unit testing,並沒有辦法測到有 middleName 的 user,因為 Unit testing 中並不存在測試 middle name 的程式碼,我們現在把這一段補上。
step1: 要先讓 unit testing failed。
step2: 換上 answer 並確保前一個 test 也通過
如果同一個專案的開發者是有寫 testing,並正確的使用 testing,那個會讓客戶閃退的程式碼,就不會發出去了。而接下來的各區 sales 的要求,在這個習慣下,也能穩定的產出符合 sales 需求的程式碼。
還記得後面的虛擬需求嗎?你可以試著寫一下這些需求的程式碼
• App 修改完後更大賣,但亞洲區 sales 反應,姓要在名字前面。且中間不要空格
• 日本 sales 反應。日本 UI 如果呈現姓加名字黏在一起,會讓特殊姓氏不知道怎麼念,而且日本人習慣姓和名中間空一格
• 馬來西亞 sales 反應,他們的中間名可能有五個起跳,但習慣上可能用不同於其他國家的方式進行呈現。所以需要先判斷 x 條件,然後符合 y 條件時,呈現 z UI。